/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package uk.co.dupenois.geometry;

import java.awt.Point;
import java.util.Comparator;

/**
 *
 * @author mpd209
 */
public class Coordinate {
    private double x;
    private double y;
    public Coordinate(double x, double y){
        this.x = x;
        this.y = y;
    }
    public double getX(){
        return x;
    }
    public double getY(){
        return y;
    }

    public void setX(double x){
        this.x = x;
    }
    public void setY(double y){
        this.y = y;
    }

    public Coordinate getLocationFromMe(double angle, double hypotenuese){
        //radians = degrees * pi/180
        //0 = 0, 360 = 2pi
        //90 = pi/2
        //180 = pi
        //270 = 3pi/2
        double targety;
        double targetx;
        if(angle == 0 || angle == 2*Math.PI){
            targety = y;
            targetx = x + hypotenuese;
            //System.out.println("Wall found at: ("+Math.round(xwall)+", "+Math.round(ywall)+")");
        }else if(angle<(Math.PI/2)){
            targetx = x + (Math.cos(angle) * hypotenuese);
            targety = y + (Math.sin(angle) * hypotenuese);
            //System.out.println("Wall found at: ("+Math.round(targetx)+", "+Math.round(targety)+")");
        }else if(angle==(Math.PI/2)){
            targety = y+hypotenuese;
            targetx = x;
        }else if(angle<Math.PI){
            double angleTo180 = Math.PI-angle;
            targetx = x - (Math.cos(angleTo180) * hypotenuese);
            targety = y + (Math.sin(angleTo180) * hypotenuese);
        }else if(angle==Math.PI){
            targety = y;
            targetx = x - hypotenuese;
        }else if(angle<(3*Math.PI/2)){
            double angleFrom180 = angle-Math.PI;
            targetx = x - (Math.cos(angleFrom180) * hypotenuese);
            targety = y - (Math.sin(angleFrom180) * hypotenuese);
        }else if(angle==(3*Math.PI/2)){
            targety = y - hypotenuese;
            targetx = x;
        }else{
            //if(angle<2*Math.PI)
            double angleTo360 = (2 * Math.PI)-angle;
            targetx = x + (Math.cos(angleTo360) * hypotenuese);
            targety = y - (Math.sin(angleTo360) * hypotenuese);
        }
        return new Coordinate(targetx, targety);
    }

    public double getDistance(Coordinate coordinate){
        double xdiffsq = Math.pow(coordinate.getX()-getX(), 2);
        double ydiffsq = Math.pow(coordinate.getY()-getY(), 2);
        return Math.sqrt(xdiffsq+ydiffsq);
    }
    public double getDistanceSquared(Coordinate coordinate){
        double xdiffsq = Math.pow(coordinate.getX()-getX(), 2);
        double ydiffsq = Math.pow(coordinate.getY()-getY(), 2);
        return xdiffsq+ydiffsq;
    }
    public double getAngleFromXAxis(Coordinate Coordinate){
        if(Coordinate.equals(this)) return 0;
        if(Coordinate.getX()==this.getX()){
            return(Coordinate.getY()>this.getY() ? Math.PI/2 : (3*Math.PI)/2);
        }
        if(Coordinate.getY()==this.getY()){
            return(Coordinate.getX()>this.getX() ? 0 : Math.PI);
        }
        double opposite;
        double adjacent;
        if(Coordinate.getX()>this.getX()){
             if(Coordinate.getY()>this.getY()){
                 //North_east
                 opposite = Coordinate.getY()-this.getY();
                 adjacent = Coordinate.getX()-this.getX();
                 return Math.atan(opposite/adjacent);
             }else{
                 //South_east
                 opposite = this.getY()-Coordinate.getY();
                 adjacent = Coordinate.getX()-this.getX();
                 return (2*Math.PI)-Math.atan(opposite/adjacent);
             }
        }else{
            if(Coordinate.getY()>this.getY()){
                 //North_west
                 opposite = Coordinate.getY()-this.getY();
                 adjacent = this.getX()-Coordinate.getX();
                 return Math.PI-Math.atan(opposite/adjacent);
             }else{
                 //South_west
                 opposite = this.getY()-Coordinate.getY();
                 adjacent = this.getX()-Coordinate.getX();
                 return Math.PI+Math.atan(opposite/adjacent);
             }
        }
    }
    public double getAngle(Coordinate point1, Coordinate point2){
        double lengthSideOP1 = this.getDistance(point1);
        double lengthSideOP2 = this.getDistance(point2);
        double lengthSideP1P2 = point1.getDistance(point2);
//        System.out.println("lengthSideOP1: "+lengthSideOP1);
//        System.out.println("lengthSideOP2: "+lengthSideOP2);
//        System.out.println("lengthSideP1P2: "+lengthSideP1P2);
        /* Angle between P1 and P2 is
         * arccos ((a^2 + b^2 -c^2)/2ab)
         * Where a is length OP1, b is OP2 and c is P1P2
         */
        double myAngle = Math.pow(lengthSideOP1, 2)+Math.pow(lengthSideOP2, 2)-Math.pow(lengthSideP1P2, 2);
//        System.out.println("(a^2 + b^2 -c^2): "+myAngle);
        myAngle = myAngle/(2*lengthSideOP1*lengthSideOP2);
//        System.out.println("(a^2 + b^2 -c^2)/2ab: "+myAngle);
        myAngle = Math.acos(myAngle);
//        System.out.println("arccos ((a^2 + b^2 -c^2)/2ab): "+myAngle);
        return myAngle;
    }

    @Override
    public boolean equals(Object other){
        if(other==this)return true;
        if(!(other instanceof Coordinate)) return false;
        Coordinate d = (Coordinate)other;
        return (d.getX()==this.getX() && d.getY()==this.getY());
    }

    /**
     * Decimal issues in with doubles mean we need to see that
     * instead of being exactly equal they're close enough that the difference
     * is probably a rounding error
     * @param other
     * @return whether or not this is close enough to other to be equal
     */
    public boolean equalsCheck(Coordinate other){
        double xDiff = Math.abs(this.getX()-other.getX());
        double yDiff = Math.abs(this.getY()-other.getY());
        double allowedDiff = 0.000009;
        return (xDiff<allowedDiff && yDiff < allowedDiff);
    }
    @Override
    public int hashCode() {
        int hash = 7;
        hash = 79 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32));
        hash = 79 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32));
        return hash;
    }

    @Override
    public String toString(){
        return "("+this.getX()+", "+this.getY()+")";
    }
    public String toString(int decimalPlaces){
        double tempx = Math.round(this.getX()*Math.pow(10, decimalPlaces))/Math.pow(10, decimalPlaces);
        double tempy = Math.round(this.getY()*Math.pow(10, decimalPlaces))/Math.pow(10, decimalPlaces);
        return "("+tempx+", "+tempy+")";
    }


    public void add(Coordinate coord){
        this.x += coord.getX();
        this.y += coord.getY();
    }

    public Point toPoint(){
        return new Point(
                (int)Math.round(getX()),
                (int)Math.round(getY())
                );
    }

    /**
     *
     * @return
     */
    @Override
    public Coordinate clone(){
        return new Coordinate(this.getX(), this.getY());
    }
    public final static Comparator<Coordinate> XYComparator = new Comparator<Coordinate>() {
        public int compare(Coordinate arg0, Coordinate arg1) {
            return arg0.compareByXY(arg1);
        }
    };

    public int compareByXY(Coordinate arg1) {
        double diff = (Math.pow(this.getX(), 2)+Math.pow(this.getY(), 2))-
                     (Math.pow(arg1.getX(), 2)+Math.pow(arg1.getY(), 2));
        if(diff == 0)return 0;
        if(diff<0)return -1;
        return 1;
    }


    public final static Comparator<Coordinate> MinXMaxYComparator = new Comparator<Coordinate>() {
        public int compare(Coordinate arg0, Coordinate arg1) {
            return arg0.compareByMinXMaxY(arg1);
        }
    };

    public int compareByMinXMaxY(Coordinate arg1){
        double diff = this.getX() - arg1.getX();
        if(diff == 0){
            diff = arg1.getY() - this.getY();
            if(diff == 0) return 0;
            if(diff<0) return -1;
            return 1;
        }
        if(diff<0)return -1;
        return 1;
    }

    public final static Comparator<Coordinate> MinXMinYComparator = new Comparator<Coordinate>() {
        public int compare(Coordinate arg0, Coordinate arg1) {
            return arg0.compareByMinXMinY(arg1);
        }
    };
    public int compareByMinXMinY(Coordinate arg1){
        double diff = this.getX() - arg1.getX();
        if(diff == 0){
            diff = this.getY() - arg1.getY();
            if(diff == 0) return 0;
            if(diff<0) return -1;
            return 1;
        }
        if(diff<0)return -1;
        return 1;
    }

    public final static Comparator<Coordinate> MaxXMaxYComparator = new Comparator<Coordinate>() {
        public int compare(Coordinate arg0, Coordinate arg1) {
            return arg0.compareByMaxXMaxY(arg1);
        }
    };
    public int compareByMaxXMaxY(Coordinate arg1){
        double diff =  arg1.getX() - this.getX();
        if(diff == 0){
            diff = arg1.getY() - this.getY();
            if(diff == 0) return 0;
            if(diff<0) return -1;
            return 1;
        }
        if(diff<0)return -1;
        return 1;
    }

    public final static Comparator<Coordinate> MaxXMinYComparator = new Comparator<Coordinate>() {
        public int compare(Coordinate arg0, Coordinate arg1) {
            return arg0.compareByMaxXMinY(arg1);
        }
    };
    public int compareByMaxXMinY(Coordinate arg1){
        double diff = arg1.getX() - this.getX();
        if(diff == 0){
            diff = this.getY() - arg1.getY();
            if(diff == 0) return 0;
            if(diff<0) return -1;
            return 1;
        }
        if(diff<0)return -1;
        return 1;
    }


    public final static Comparator<Coordinate> MinYMaxXComparator = new Comparator<Coordinate>() {
        public int compare(Coordinate arg0, Coordinate arg1) {
            return arg0.compareByMinYMaxX(arg1);
        }
    };
    public int compareByMinYMaxX(Coordinate arg1){
        double diff = this.getY() - arg1.getY();
        if(diff == 0){
            diff = arg1.getX() - this.getX();
            if(diff == 0) return 0;
            if(diff<0) return -1;
            return 1;
        }
        if(diff<0)return -1;
        return 1;
    }
    public final static Comparator<Coordinate> MinYMinXComparator = new Comparator<Coordinate>() {
        public int compare(Coordinate arg0, Coordinate arg1) {
            return arg0.compareByMinYMinX(arg1);
        }
    };
    public int compareByMinYMinX(Coordinate arg1){
        double diff = this.getY() - arg1.getY();
        if(diff == 0){
            diff = this.getX() - arg1.getX();
            if(diff == 0) return 0;
            if(diff<0) return -1;
            return 1;
        }
        if(diff<0)return -1;
        return 1;
    }

    public final static Comparator<Coordinate> MaxYMaxXComparator = new Comparator<Coordinate>() {
        public int compare(Coordinate arg0, Coordinate arg1) {
            return arg0.compareByMaxYMaxX(arg1);
        }
    };
    public int compareByMaxYMaxX(Coordinate arg1){
        double diff = arg1.getY() - this.getY();
        if(diff == 0){
            diff = arg1.getX() - this.getX();
            if(diff == 0) return 0;
            if(diff<0) return -1;
            return 1;
        }
        if(diff<0)return -1;
        return 1;
    }
    public final static Comparator<Coordinate> MaxYMinXComparator = new Comparator<Coordinate>() {
        public int compare(Coordinate arg0, Coordinate arg1) {
            return arg0.compareByMaxYMinX(arg1);
        }
    };
    public int compareByMaxYMinX(Coordinate arg1){
        double diff = arg1.getY() - this.getY();
        if(diff == 0){
            diff = this.getX() - arg1.getX();
            if(diff == 0) return 0;
            if(diff<0) return -1;
            return 1;
        }
        if(diff<0)return -1;
        return 1;
    }


}
